package com.consume.annotation;

/**
 * @author zhangbowen@heytea.com
 * @since 2019-08-26 16:28
 */

import brave.propagation.CurrentTraceContext;
import brave.propagation.TraceContext;
import com.consume.controller.TraceScope;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.cglib.core.SpringNamingPolicy;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

@Component //注意：Bean后置处理器本身也是一个Bean
public class TraceAnnotationBeanPostProcessor implements BeanPostProcessor, InitializingBean {
    private String innerClassName;

    @Override
    public void afterPropertiesSet() throws Exception {

    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        Method[] declaredMethods = bean.getClass().getMethods();
        for (Method declaredMethod : declaredMethods) {
            TraceId annotation = declaredMethod.getAnnotation(TraceId.class);
            if (null == annotation) {
                continue;
            }
            declaredMethod.setAccessible(true);

            Class innerClass = declaredMethod.getDeclaringClass();
            if (innerClass.isInterface()) {
                return InterfaceProxy.newInstance(innerClass);
            } else {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(innerClass);
                enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
                enhancer.setCallback(new MethodInterceptorImpl());
                return enhancer.create();
            }
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
        //这里要返回o，不然启动时会报错
        return o;
    }

    public static class InterfaceProxy implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("ObjectProxy execute:" + method.getName());
            return method.invoke(proxy, args);
        }

        public static <T> T newInstance(Class<T> innerInterface) {
            ClassLoader classLoader = innerInterface.getClassLoader();
            Class[] interfaces = new Class[]{innerInterface};
            InterfaceProxy proxy = new InterfaceProxy();
            return (T) Proxy.newProxyInstance(classLoader, interfaces, proxy);
        }
    }

    public static class MethodInterceptorImpl implements MethodInterceptor {

        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
           /* System.out.println("MethodInterceptorImpl:" + method.getName());
            return methodProxy.invokeSuper(o, objects);*/
            try (CurrentTraceContext.Scope scope = TraceScope.newScope(null)) {
                return methodProxy.invokeSuper(o, objects);
            }
        }
    }
}